﻿using Neo4j.Driver;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;


namespace Neo4JConnector.Core
{

    public class Neo4jTypeConverter
    {
        public static object ConvertToCommonType(object neo4jValue)
        {
            if (neo4jValue == null) return null;

            return neo4jValue switch
            {
                INode node => ConvertNode(node),
                IRelationship relationship => ConvertRelationship(relationship),
                IPath path => ConvertPath(path),
                ZonedDateTime zoned => ConvertZonedDateTime(zoned),
                LocalDateTime local => ConvertLocalDateTime(local),
                LocalTime localTime => ConvertLocalTime(localTime),
                Duration duration => ConvertDuration(duration),
                Point point => ConvertPoint(point),
                _ => neo4jValue
            };
        }

        private static Dictionary<string, object> ConvertNode(INode node)
        {
            return new Dictionary<string, object>
            {
                ["id"] = GetPropertyValue(node.Properties, "id")?.ToString(),
                ["labels"] = node.Labels,
                ["properties"] = ConvertProperties(node.Properties)
            };
        }

        private static Dictionary<string, object> ConvertRelationship(IRelationship relationship)
        {
            return new Dictionary<string, object>
            {
                ["id"] = GetPropertyValue(relationship.Properties, "id")?.ToString(),
                ["type"] = relationship.Type,
                ["startNodeId"] = relationship.StartNodeId,
                ["endNodeId"] = relationship.EndNodeId,
                ["properties"] = ConvertProperties(relationship.Properties)
            };
        }

        private static Dictionary<string, object> ConvertPath(IPath path)
        {
            return new Dictionary<string, object>
            {
                ["nodes"] = path.Nodes.Select(ConvertNode).ToList(),
                ["relationships"] = path.Relationships.Select(ConvertRelationship).ToList(),
                ["startNode"] = ConvertNode(path.Start),
                ["endNode"] = ConvertNode(path.End)
            };
        }

        private static DateTimeOffset ConvertZonedDateTime(ZonedDateTime zoned)
        {
            // 使用反射安全地访问属性
            int year = (int)GetPropertyValue(zoned, "Year");
            int month = (int)GetPropertyValue(zoned, "Month");
            int day = (int)GetPropertyValue(zoned, "Day");
            int hour = (int)GetPropertyValue(zoned, "Hour");
            int minute = (int)GetPropertyValue(zoned, "Minute");
            int second = (int)GetPropertyValue(zoned, "Second");
            int nanosecond = (int)GetPropertyValue(zoned, "Nanosecond");
            int offsetSeconds = (int)GetPropertyValue(zoned, "OffsetSeconds");

            var dateTime = new DateTime(year, month, day, hour, minute, second, nanosecond / 1000000);
            return new DateTimeOffset(dateTime, TimeSpan.FromSeconds(offsetSeconds));
        }

        private static DateTime ConvertLocalDateTime(LocalDateTime local)
        {
            int year = (int)GetPropertyValue(local, "Year");
            int month = (int)GetPropertyValue(local, "Month");
            int day = (int)GetPropertyValue(local, "Day");
            int hour = (int)GetPropertyValue(local, "Hour");
            int minute = (int)GetPropertyValue(local, "Minute");
            int second = (int)GetPropertyValue(local, "Second");
            int nanosecond = (int)GetPropertyValue(local, "Nanosecond");

            return new DateTime(year, month, day, hour, minute, second, nanosecond / 1000000);
        }

        private static TimeSpan ConvertLocalTime(LocalTime localTime)
        {
            int hour = (int)GetPropertyValue(localTime, "Hour");
            int minute = (int)GetPropertyValue(localTime, "Minute");
            int second = (int)GetPropertyValue(localTime, "Second");
            int nanosecond = (int)GetPropertyValue(localTime, "Nanosecond");

            return new TimeSpan(0, hour, minute, second, nanosecond / 1000000);
        }

        private static object ConvertDuration(Duration duration)
        {
            // 使用反射安全地访问属性
            int months = (int)GetPropertyValue(duration, "Months");
            int days = (int)GetPropertyValue(duration, "Days");
            int seconds = (int)GetPropertyValue(duration, "Seconds");

            // 尝试获取纳秒属性（不同版本可能有不同名称）
            int nanoseconds = 0;
            try
            {
                // 尝试不同的属性名称
                var nanos = GetPropertyValue(duration, "Nanos");
                if (nanos != null) nanoseconds = (int)nanos;
            }
            catch
            {
                try
                {
                    var nanosecondsProp = GetPropertyValue(duration, "Nanoseconds");
                    if (nanosecondsProp != null) nanoseconds = (int)nanosecondsProp;
                }
                catch
                {
                    // 如果两个属性都不存在，保持为0
                }
            }

            // 根据duration的特性提供多种表示方式
            if (months == 0 && days == 0)
            {
                // 纯时间间隔，可以转换为TimeSpan
                return TimeSpan.FromSeconds(seconds + nanoseconds / 1000000000.0);
            }
            else
            {
                // 包含日期成分，保持原始格式
                return new Dictionary<string, object>
                {
                    ["months"] = months,
                    ["days"] = days,
                    ["seconds"] = seconds,
                    ["nanoseconds"] = nanoseconds
                };
            }
        }

        private static Dictionary<string, object> ConvertPoint(Point point)
        {
            var result = new Dictionary<string, object>();

            // 使用反射安全地访问属性
            try
            {
                result["x"] = GetPropertyValue(point, "X");
            }
            catch { }

            try
            {
                result["y"] = GetPropertyValue(point, "Y");
            }
            catch { }

            try
            {
                result["z"] = GetPropertyValue(point, "Z");
            }
            catch { }

            // 尝试获取CRS信息（不同版本可能有不同属性名）
            try
            {
                var crs = GetPropertyValue(point, "Crs");
                if (crs != null) result["crs"] = crs;
            }
            catch
            {
                try
                {
                    var srid = GetPropertyValue(point, "SrId");
                    if (srid != null) result["srid"] = srid;
                }
                catch
                {
                    // 如果两个属性都不存在，忽略
                }
            }

            return result;
        }

        private static Dictionary<string, object> ConvertProperties(IReadOnlyDictionary<string, object> properties)
        {
            var result = new Dictionary<string, object>();
            foreach (var kvp in properties)
            {
                result[kvp.Key] = ConvertToCommonType(kvp.Value);
            }
            return result;
        }

        // 通用的属性访问方法
        private static object GetPropertyValue(object obj, string propertyName)
        {
            if (obj == null) return null;

            var property = obj.GetType().GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance);
            if (property != null && property.CanRead)
            {
                return property.GetValue(obj);
            }

            // 如果属性不存在，尝试查找替代名称
            var alternativeNames = GetAlternativePropertyNames(propertyName);
            foreach (var altName in alternativeNames)
            {
                property = obj.GetType().GetProperty(altName, BindingFlags.Public | BindingFlags.Instance);
                if (property != null && property.CanRead)
                {
                    return property.GetValue(obj);
                }
            }

            throw new InvalidOperationException($"Property '{propertyName}' not found on type '{obj.GetType().Name}'");
        }

        // 属性名称的替代映射
        private static IEnumerable<string> GetAlternativePropertyNames(string propertyName)
        {
            return propertyName.ToLower() switch
            {
                "nanoseconds" => new[] { "Nanos", "Nanoseconds" },
                "nanos" => new[] { "Nanoseconds", "Nanos" },
                "crs" => new[] { "SrId", "Crs" },
                "srid" => new[] { "Crs", "SrId" },
                _ => new[] { propertyName }
            };
        }
    }
}
